package com.itheima.stock.service.impl;

import com.google.common.collect.Lists;
import com.itheima.stock.constants.ParseType;
import com.itheima.stock.mapper.StockBlockRtInfoMapper;
import com.itheima.stock.mapper.StockBusinessMapper;
import com.itheima.stock.mapper.StockMarketIndexInfoMapper;
import com.itheima.stock.mapper.StockRtInfoMapper;
import com.itheima.stock.pojo.domain.StockInfoConfig;
import com.itheima.stock.pojo.entity.StockBlockRtInfo;
import com.itheima.stock.pojo.entity.StockMarketIndexInfo;
import com.itheima.stock.service.StockTimerTaskService;
import com.itheima.stock.utils.DateTimeUtil;
import com.itheima.stock.utils.IdWorker;
import com.itheima.stock.utils.ParserStockInfoUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.joda.time.DateTime;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Service("stockTimerTaskService")
@Slf4j
public class StockTimerTaskServiceImpl implements StockTimerTaskService {

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private StockInfoConfig stockInfoConfig;

    @Autowired
    private IdWorker idWorker;
    @Autowired
    private StockMarketIndexInfoMapper stockMarketIndexInfoMapper;
    @Resource
    private StockRtInfoMapper stockRtInfoMapper;
    // 必须保证该对象无状态
    private HttpEntity<Object> entity;
    @Resource
    private StockBlockRtInfoMapper stockBlockRtInfoMapper;
    @Resource
    private RabbitTemplate rabbitTemplate;

    @Override
    public void getInnerMarketInfo() {
        //1.定义采集的url接口
        String url = stockInfoConfig.getMarketUrl() + String.join(",", stockInfoConfig.getInner());
        //2.调用restTemplate采集数据
        //2.1 组装请求头
//        HttpHeaders headers = new HttpHeaders();
//        //必须填写，否则数据采集不到
//        headers.add("Referer","https://finance.sina.com.cn/stock/");
//        headers.add("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0");
//        //2.2 组装请求对象
//        HttpEntity<Object> entity = new HttpEntity<>(headers);
        //2.3 resetTemplate发起请求
        ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
        int statusCodeValue = responseEntity.getStatusCodeValue();
        if (statusCodeValue != 200) {
            log.error("采集失败，状态码为：{}", statusCodeValue);
            return;
        }
        String jsData = responseEntity.getBody();
        String reg = "var hq_str_(.+)=\"(.+)\";";
        //编译表达式,获取编译对象
        Pattern pattern = Pattern.compile(reg);
        //匹配字符串
        Matcher matcher = pattern.matcher(jsData);
        ArrayList<StockMarketIndexInfo> list = new ArrayList<>();
        //判断是否有匹配的数值
        while (matcher.find()) {
            //获取大盘的code
            String marketCode = matcher.group(1);
            //获取其它信息，字符串以逗号间隔
            String otherInfo = matcher.group(2);
            //以逗号切割字符串，形成数组
            String[] splitArr = otherInfo.split(",");
            //大盘名称
            String marketName = splitArr[0];
            //获取当前大盘的开盘点数
            BigDecimal openPoint = new BigDecimal(splitArr[1]);
            //前收盘点
            BigDecimal preClosePoint = new BigDecimal(splitArr[2]);
            //获取大盘的当前点数
            BigDecimal curPoint = new BigDecimal(splitArr[3]);
            //获取大盘最高点
            BigDecimal maxPoint = new BigDecimal(splitArr[4]);
            //获取大盘的最低点
            BigDecimal minPoint = new BigDecimal(splitArr[5]);
            //获取成交量
            Long tradeAmt = Long.valueOf(splitArr[8]);
            //获取成交金额
            BigDecimal tradeVol = new BigDecimal(splitArr[9]);
            //时间
            Date curTime = DateTimeUtil.getDateTimeWithoutSecond(splitArr[30] + " " + splitArr[31]).toDate();
            //组装entity对象
            StockMarketIndexInfo info = StockMarketIndexInfo.builder()
                    .id(idWorker.nextId())
                    .marketCode(marketCode)
                    .marketName(marketName)
                    .curPoint(curPoint)
                    .openPoint(openPoint)
                    .preClosePoint(preClosePoint)
                    .maxPoint(maxPoint)
                    .minPoint(minPoint)
                    .tradeVolume(tradeVol)
                    .tradeAmount(tradeAmt)
                    .curTime(curTime)
                    .build();
            //收集封装的对象，方便批量插入
            list.add(info);
        }
        log.info("采集的当前大盘数据：{}", list);
        //批量插入
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        int count = this.stockMarketIndexInfoMapper.insertBatch(list);
        if (count > 0) {
            log.info("批量插入了：{}条数据", count);
            //通知后台终端刷新本地缓存，发送的日期数据是告知对方当前更新的股票数据所在时间点
            rabbitTemplate.convertAndSend("stockExchange", "inner.market", new Date());

        } else {
            log.error("批量插入失败,当前时间：{}", DateTime.now().toString("yyyy-MM-dd HH:mm:ss"));
        }
    }

    //注入格式解析bean
    @Autowired
    private ParserStockInfoUtil parserStockInfoUtil;

    @Autowired
    private StockBusinessMapper stockBusinessMapper;
    @Resource
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    /**
     * 批量获取股票分时数据详情信息
     * http://hq.sinajs.cn/list=sz000002,sh600015
     */
    @Override
    public void getStockRtIndex() {
        //批量获取股票ID集合
        List<String> stockIds = stockBusinessMapper.getStockIds();
        //计算出符合sina命名规范的股票id数据
        stockIds = stockIds.stream().map(id -> {
            return id.startsWith("6") ? "sh" + id : "sz" + id;
        }).collect(Collectors.toList());
        //一次性查询过多，我们将需要查询的数据先进行分片处理，每次最多查询20条股票数据
        long startTime = System.currentTimeMillis();
        Lists.partition(stockIds, 15).forEach(info -> {
            /*// 分批次采集
            String url = stockInfoConfig.getMarketUrl() + String.join(",", info);
            //  维护请求头，添加防盗链和用户标识
//            HttpHeaders headers = new HttpHeaders();
//            headers.add("Referer","http://finance.sina.com.cn/stock/");
//            headers.add("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0");
//            // 请求实体对象
//            HttpEntity<Object> entity = new HttpEntity<>(headers);
            ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
            int statusCodeValue = responseEntity.getStatusCodeValue();
            if (statusCodeValue != 200){
                log.error("采集失败，状态码为：{}",statusCodeValue);
                return;
            }
            // 获取原始js数据
            String jsData = responseEntity.getBody();
            List list = parserStockInfoUtil.parser4StockOrMarketInfo(jsData, ParseType.ASHARE);
            log.info("采集的分时数据：{}",list);
            // 批量插入
            int count = stockRtInfoMapper.insertBatch(list);
            if (count > 0){
                log.info("批量插入个股{}条数据",count);
            }else{
                log.error("批量个股插入失败");
            }*/
            // 原始方案一 ： 采集个股数据时将集合分片，然后费批次穿行采集数据，效率低，存在较高的采集延迟，引入多线程
            // 问题：1、每次的任务都会创建一个线程，复用性差，
            // 2 、如果多线程使用不当，会导致线程池资源耗尽，从而影响系统的可用性。
            /*new Thread(()->{
                // 分批次采集
            String url = stockInfoConfig.getMarketUrl() + String.join(",", info);
            //  维护请求头，添加防盗链和用户标识
//            HttpHeaders headers = new HttpHeaders();
//            headers.add("Referer","http://finance.sina.com.cn/stock/");
//            headers.add("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0");
//            // 请求实体对象
//            HttpEntity<Object> entity = new HttpEntity<>(headers);
            ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
            int statusCodeValue = responseEntity.getStatusCodeValue();
            if (statusCodeValue != 200){
                log.error("采集失败，状态码为：{}",statusCodeValue);
                return;
            }
            // 获取原始js数据
            String jsData = responseEntity.getBody();
            List list = parserStockInfoUtil.parser4StockOrMarketInfo(jsData, ParseType.ASHARE);
            log.info("采集的分时数据：{}",list);
            // 批量插入
            int count = stockRtInfoMapper.insertBatch(list);
            if (count > 0){
                log.info("批量插入个股{}条数据",count);
            }else{
                log.error("批量个股插入失败");
            }
            }).start();*/

            // 方案二 ： 引入线程池
            threadPoolTaskExecutor.execute(() -> {
                // 分批次采集
                String url = stockInfoConfig.getMarketUrl() + String.join(",", info);
                //  维护请求头，添加防盗链和用户标识
//            HttpHeaders headers = new HttpHeaders();
//            headers.add("Referer","http://finance.sina.com.cn/stock/");
//            headers.add("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0");
//            // 请求实体对象
//            HttpEntity<Object> entity = new HttpEntity<>(headers);
                ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
                int statusCodeValue = responseEntity.getStatusCodeValue();
                if (statusCodeValue != 200) {
                    log.error("采集失败，状态码为：{}", statusCodeValue);
                    return;
                }
                // 获取原始js数据
                String jsData = responseEntity.getBody();
                List list = parserStockInfoUtil.parser4StockOrMarketInfo(jsData, ParseType.ASHARE);
                log.info("采集的分时数据：{}", list);
                // 批量插入
                int count = stockRtInfoMapper.insertBatch(list);
                if (count > 0) {
                    log.info("批量插入个股{}条数据", count);
                } else {
                    log.error("批量个股插入失败");
                }
            });
        });
        long taskTime = System.currentTimeMillis() - startTime;
        log.info("采集个股数据耗时：{}", taskTime);
    }

    @Override
    public void getStockBlock() {
        String url = "https://vip.stock.finance.sina.com.cn/q/view/newSinaHy.php";
        ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
        int statusCodeValue = responseEntity.getStatusCodeValue();
        if (statusCodeValue != 200) {
            log.error("采集失败，状态码为：{}", statusCodeValue);
            return;
        }
        // 获取原始js数据
        String jsData = responseEntity.getBody();
        String data = jsData.split("=")[1];
        System.out.println(data);
        Map<String, String> map = new HashMap<>();
        Pattern pattern = Pattern.compile("(\"[^\"]+?\"):\\\"(.+?)\\\"(?=,|$)");
        Matcher matcher = pattern.matcher(data);

        while (matcher.find()) {
            String key = matcher.group(1).replace("\"", "");
            String value = matcher.group(2).replace("\"", "");
            map.put(key, value);
        }
        List<StockBlockRtInfo> collect = (List<StockBlockRtInfo>) map.values().stream().map(restStr -> {
            String infos = (String) restStr;
            String[] infoArray = infos.split(",");
            //板块编码
            String label = infoArray[0];
            //板块行业
            String blockName = infoArray[1];
            //板块公司数量
            Integer companyNum = Integer.valueOf(infoArray[2]);
            //均价
            BigDecimal avgPrice = new BigDecimal(infoArray[3]);
            //涨跌幅度
            BigDecimal priceLimit = new BigDecimal(infoArray[5]);
            //涨跌率
            //BigDecimal updownRate=new BigDecimal(infoArray[5]);
            //总成交量
            Long tradeAmount = Long.valueOf(infoArray[6]);
            //总成交金额
            BigDecimal tradeVolume = new BigDecimal(infoArray[7]);
            //当前日期
            Date now = DateTimeUtil.getDateTimeWithoutSecond(DateTime.now()).toDate();
            //构建板块信息对象
            StockBlockRtInfo blockRtInfo = StockBlockRtInfo.builder().id(idWorker.nextId()).label(label)
                    .blockName(blockName).companyNum(companyNum).avgPrice(avgPrice).curTime(now)
                    .updownRate(priceLimit).tradeAmount(tradeAmount).tradeVolume(tradeVolume)
                    .build();
            return blockRtInfo;
        }).collect(Collectors.toList());
        collect.forEach(System.out::println);
        int count = stockBlockRtInfoMapper.insertBatch(collect);

        if (count > 0) {
            log.info("批量板块{}条数据", count);
        } else {
            log.error("批量板块插入失败");
        }
    }

    // bean生命周期的初始化方法
    @PostConstruct
    public void initData() {
        HttpHeaders headers = new HttpHeaders();
        //必须填写，否则数据采集不到
        headers.add("Referer", "https://finance.sina.com.cn/stock/");
        headers.add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0");
        //2.2 组装请求对象
        entity = new HttpEntity<>(headers);
    }

} 